import threading

from flask import Flask
import requests
import random
import time

FOLLOWER = 0
CANDIDATE = 1
LEADER = 2

app = Flask(__name__)

class RaftNodeService:
    def __init__(self, nodeId, host, port, nodes):
        self.app = Flask(__name__)
        self.id = nodeId
        self.term = 0
        self.voteFor = -1
        self.lastTime = time.time()
        self.timeOut = random.uniform(1, 5)
        self.leader = -1
        self.role = FOLLOWER
        self.nodes = nodes
        self.host = host
        self.port = port
        self.join(f"{host}:{port}")

    def registerRoutes(self):
        self.app.add_url_rule("/vote/<int:term>/<int:candidate>", 'vote', self.vote)
        self.app.add_url_rule("/heartBeat/<int:term>/<int:leader>", 'heartBeat', self.heartBeat)
        self.app.add_url_rule("/broadcast-nodes/<nodes>", 'broadcast-nodes', self.refreshByBroadcastNodes)
        self.app.add_url_rule("/join/<node>", 'join', self.join)

    def join(self, node):
        if node not in self.nodes:
            print(f"新节点加入 {node} ")
            self.nodes.append(node)
        if self.role == LEADER:
            print(f"广播节点信息 {','.join(self.nodes)}")
            for node in self.nodes:
                if node != f"{self.host}:{self.port}":
                    try:
                        requests.get(f"http://{node}/broadcast-nodes/{','.join(self.nodes)}")
                    except Exception as e:
                        print(e)
            try:
                requests.get(f"http://{node}/broadcast-nodes/{','.join(self.nodes)}")
            except Exception as e:
                print(e)
        return "success"

    def refreshByBroadcastNodes(self, nodes):
        print(f"刷新节点信息 {nodes}")
        self.nodes = nodes.split(",")
        return "success"

    def heartBeat(self, term, leader):
        self.lastTime = time.time()
        self.term = max(term, self.term)
        self.leader = leader
        if self.role == LEADER:
            self.role = FOLLOWER
        print(f"监测到来自 {leader} 的心跳")
        return "success"

    def vote(self, term, candidate):
        if self.term > term or self.voteFor >= 0 or self.leader >= 0:
            print(f"拒绝投票给 {candidate}")
            return "false"
        self.term = term
        self.voteFor = candidate
        print(f"投票给 {candidate}")
        return "true"

    def ascend(self):
        if self.role == LEADER:
            for node in self.nodes:
                if node != f"{self.host}:{self.port}":
                    try:
                        requests.get(f"http://{node}/heartBeat/{self.term}/{self.id}")
                    except Exception as e:
                        print(e)
            threading.Timer(0.1, lambda :self.ascend()).start()

    def startElection(self):
        if self.role == FOLLOWER:
            self.leader = -1
            self.role = CANDIDATE
            self.voteFor = self.id
            self.term += 1
            tickets = 1
            print(f"Term {self.term} 发起投票")
            for node in self.nodes:
                if node != f"{self.host}:{self.port}":
                    try:
                        response = requests.get(f"http://{node}/vote/{self.term}/{self.id}")
                        if response.status_code == 200 and response.content.decode() == 'true':
                            tickets += 1
                    except Exception as e:
                        print(e)
            print(f"Term {self.term} 共得票 {tickets}/{len(self.nodes)}")
            if tickets > len(self.nodes) / 2:
                print(f"Term {self.term} 竞选成功")
                self.role = LEADER
                self.ascend()
            else:
                print(f"Term {self.term} 竞选失败")
                self.voteFor = -1
                self.leader = -1
                self.role = FOLLOWER

    def register(self):
        for node in self.nodes:
            if node != f"{self.host}:{self.port}":
                try:
                    requests.get(f"http://{node}/join/{self.host}:{self.port}")
                except Exception as e:
                    print(e)


    def waitToElect(self):
        if self.lastTime + self.timeOut < time.time() and self.role == FOLLOWER:
            self.startElection()
            self.lastTime = time.time()
        threading.Timer(0.1, lambda :self.waitToElect()).start()

    def start(self):
        self.registerRoutes()
        threading.Timer(3, lambda: self.waitToElect()).start()
        threading.Timer(3, lambda: self.register()).start()
        self.app.run(self.host, self.port, threaded=True)

if __name__ == '__main__':
    print("before")
    RaftNodeService(1, "localhost", 9992, []).start()
